﻿#ifndef FUNC_H
#define FUNC_H

#include <map>
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <io.h>
#include "Windows.h"
using std::map;
using std::vector;
using std::string;

namespace func
{
    std::wstring utf8_to_unicode(const std::string& utf8string);
    std::string unicode_to_utf8(const std::wstring& widestring);
    string unicode_to_ascii(std::wstring& wstrcode);
    std::wstring ascii_to_unicode(string& strascii);
    string utf8_to_ascii(string& utf8string);
    string ascii_to_utf8(string& strAsciiCode);

    template < class T >
    string toString(const T& n);
    const int atoI(string s);

    string encode(vector<int> keys);
    vector<int> decode(const string& code);

    string lower(string s);
    string trim(string s);
    bool find_command(vector<string>& v, string command);
    void split(const string& s, const string& c, vector<string>& v, bool isSort = true);
    map<string, string> analysis(vector<string>& v, string command);
    auto find_key(map<string, string>& m);
    bool mode(map<string, string>& m);
    bool exist(const char* url);
    template < class T >
    void unique(vector<T>& v);
    string& replace_all(string& str, const string& old_value, const string& new_value);
    void read_file(const char* txt);

    //define
    std::wstring utf8_to_unicode(const std::string& utf8string) {
        int widesize = ::MultiByteToWideChar(CP_UTF8, 0, utf8string.c_str(), -1, NULL, 0);
        if (widesize == ERROR_NO_UNICODE_TRANSLATION) {
            throw std::exception("Invalid UTF-8 sequence.");
        }
        if (widesize == 0) {
            throw std::exception("Error in conversion.");
        }
        std::vector<wchar_t> resultstring(widesize);
        int convresult = ::MultiByteToWideChar(CP_UTF8, 0, utf8string.c_str(), -1, &resultstring[0], widesize);
        if (convresult != widesize) {
            throw std::exception("La falla!");
        }
        return std::wstring(&resultstring[0]);
    }
    std::string unicode_to_utf8(const std::wstring& widestring) {
        int utf8size = ::WideCharToMultiByte(CP_UTF8, 0, widestring.c_str(), -1, NULL, 0, NULL, NULL);
        if (utf8size == 0) {
            throw std::exception("Error in conversion.");
        }
        std::vector<char> resultstring(utf8size);
        int convresult = ::WideCharToMultiByte(CP_UTF8, 0, widestring.c_str(), -1, &resultstring[0], utf8size, NULL, NULL);
        if (convresult != utf8size) {
            throw std::exception("La falla!");
        }
        return std::string(&resultstring[0]);
    }
    string unicode_to_ascii(std::wstring& wstrcode) {
        int asciisize = ::WideCharToMultiByte(CP_OEMCP, 0, wstrcode.c_str(), -1, NULL, 0, NULL, NULL);
        if (asciisize == ERROR_NO_UNICODE_TRANSLATION) {
            throw std::exception("Invalid UTF-8 sequence.");
        }
        if (asciisize == 0) {
            throw std::exception("Error in conversion.");
        }
        std::vector<char> resultstring(asciisize);
        int convresult = ::WideCharToMultiByte(CP_OEMCP, 0, wstrcode.c_str(), -1, &resultstring[0], asciisize, NULL, NULL);
        if (convresult != asciisize) {
            throw std::exception("La falla!");
        }
        return std::string(&resultstring[0]);
    }
    std::wstring ascii_to_unicode(string& strascii) {
        int widesize = MultiByteToWideChar(CP_ACP, 0, (char*)strascii.c_str(), -1, NULL, 0);
        if (widesize == ERROR_NO_UNICODE_TRANSLATION) {
            throw std::exception("Invalid UTF-8 sequence.");
        }
        if (widesize == 0) {
            throw std::exception("Error in conversion.");
        }
        std::vector<wchar_t> resultstring(widesize);
        int convresult = MultiByteToWideChar(CP_ACP, 0, (char*)strascii.c_str(), -1, &resultstring[0], widesize);
        if (convresult != widesize) {
            throw std::exception("La falla!");
        }
        return std::wstring(&resultstring[0]);
    }
    string utf8_to_ascii(string& utf8string) {
        string strRet("");
        std::wstring wstr = utf8_to_unicode(utf8string);
        strRet = unicode_to_ascii(wstr);
        return strRet;
    }
    string ascii_to_utf8(string& strAsciiCode) {
        string strRet("");
        std::wstring wstr = ascii_to_unicode(strAsciiCode);
        strRet = unicode_to_utf8(wstr);
        return strRet;
    }

    template < class T >
    inline string toString(const T& n)
    {
        std::ostringstream stm;
        stm << n;
        return stm.str();
    }
    inline const int atoI(string s)
    {
        std::istringstream is(s);
        int i;
        is >> i;
        return i;
    }

    inline string encode(vector<int> keys)
    {
        std::sort(keys.begin(), keys.end());
        string s;
        for (auto key : keys)
        {
            s.append(" ");
            s.append(toString(key));
        }
        return s;
    }
    inline vector<int> decode(const string& code)
    {
        vector<int> v;
        string::size_type index = code.find_first_of(' ');
        while (index != string::npos)
        {
            const string::size_type i = code.find(' ', index + 1);
            int key = atoI(code.substr(index + 1, i - index - 1));
            v.push_back(key);
            index = i;
        }
        std::sort(v.begin(), v.end());
        return v;
    }

    inline string lower(string s)
    {
        const int len = s.size();
        for (int i = 0; i < len; i++)
        {
            if (s[i] >= 'A' && s[i] <= 'Z')
            {
                s[i] += 32;
            }
        }
        return s;
    }
    inline string trim(string s)
    {
        int index = 0;
        if (!s.empty())
        {
            while ((index = s.find(' ', index)) != string::npos)
            {
                s.erase(index, 1);
            }
        }
        return s;
    }
    bool find_command(vector<string>& commands, string command)
    {
        for (auto& c : commands)
        {
            if (command == lower(c))
            {
                return true;
            }
        }
        return false;
    }
    inline void split(const string& s, const string& c, vector<string>& v, bool isSort)
    {
        v.clear();
        string::size_type pos1, pos2;
        pos2 = s.find(c);
        pos1 = 0;
        while (string::npos != pos2)
        {
            v.push_back(s.substr(pos1, pos2 - pos1));
            pos1 = pos2 + c.size();
            pos2 = s.find(c, pos1);
        }
        if (pos1 != s.length())
            v.push_back(s.substr(pos1));
        if (isSort)
        {
            func::unique(v);
        }
    }
    inline map<string, string> analysis(vector<string>& v, string command)
    {
        map<string, string> orders;
        for (auto i = 0; i < v.size(); i++)
        {
            auto index = v[i].find(":");
            if (index == string::npos)
            {
                orders.insert(map<string, string>::value_type(v[i], string()));
            }
            else
            {
                orders.insert(map<string, string>::value_type(v[i].substr(0, index), v[i].substr(index + 1)));
            }
        }
        orders.erase(command);
        return orders;
    }
    inline auto find_key(map<string, string>& m)
    {
        string options[4] = { "account","key","password","record" };
        if (m.size() == 1) {
            auto it = m.end();
            for (int i = 0; it == m.end(); i++)
            {
                it = m.find(options[i]);
            }
            if (it == m.end())
            {
                throw "too few parameters.\n";
            }
            else
            {
                return it;
            }
        }
        else
        {
            throw "too many parameters.\n";
        }
    }
    bool mode(map<string, string>& m)
    {
        bool isfoggy = true;
        auto it = m.find("exact");
        if (it != m.end())
        {
            isfoggy = false;
            m.erase("exact");
        }
        m.erase("foggy");
        return isfoggy;
    }
    inline bool exist(const char* url)
    {
        return _access(url, 6) == 0;
    }
    template<class T>
    inline void unique(vector<T>& v)
    {
        std::sort(v.begin(), v.end());
        auto it = std::unique(v.begin(), v.end());
        v.erase(it, v.end());
    }
    inline string& replace_all(string& str, const string& old_value, const string& new_value)
    {
        while (true) {
            string::size_type   pos(0);
            if ((pos = str.find(old_value)) != string::npos)
                str.replace(pos, old_value.length(), new_value);
            else
                break;
        }
        return str;
    }
    void read_file(const char * txt)
    {
        std::ifstream file(txt);
        string temp;
        if (!file.is_open())
        {
            throw "can't find help document.\n";
        }
        while (std::getline(file, temp))
        {
            std::cout << temp << '\n';
        }
        file.close();
        std::cout << std::endl;
    }
}

#endif